home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_300
/
333_01
/
awk2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-04-21
|
33KB
|
1,232 lines
/*
* awk2 --- gawk parse tree interpreter
*
* Copyright (C) 1986 Free Software Foundation
* Written by Paul Rubin, August 1986
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <setjmp.h>
#include "awk.h"
/* More of that debugging stuff */
#ifdef FAST
#define DEBUG(X)
#else
#define DEBUG(X) print_debug X
#endif
/* longjmp return codes, must be nonzero */
/* Continue means either for loop/while continue, or next input record */
#define TAG_CONTINUE 1
/* Break means either for/while break, or stop reading input */
#define TAG_BREAK 2
/* the loop_tag_valid variable allows continue/break-out-of-context
* to be caught and diagnosed (jfw) */
#define PUSH_BINDING(stack, x) \
(memcpy((stack), (x), sizeof(jmp_buf)), loop_tag_valid++)
#define RESTORE_BINDING(stack, x) \
(memcpy((x), (stack), sizeof(jmp_buf)), loop_tag_valid--)
/* for "for(iggy in foo) {" */
struct search
{
int numleft;
AHASH **arr_ptr;
AHASH *bucket;
NODE *symbol;
NODE *retval;
};
STATIC struct search * NEAR PASCAL assoc_scan(NODE *symbol);
STATIC struct search * NEAR PASCAL assoc_next(struct search *lookat);
/* Tree is a bunch of rules to run.
Returns zero if it hit an exit() statement */
int PASCAL interpret(NODE *tree)
{
register NODE *t; /* temporary */
auto jmp_buf loop_tag_stack; /* shallow binding stack for loop_tag */
static jmp_buf loop_tag; /* always the current binding */
static int loop_tag_valid = 0; /* nonzero when loop_tag valid (jfw) */
static jmp_buf rule_tag; /* tag the rule currently being run, for NEXT
* and EXIT statements. It is static because
* there are no nested rules */
register NODE **lhs; /* lhs == Left Hand Side for assigns, etc */
register struct search *l; /* For array_for */
/* clean up temporary strings created by evaluating expressions in
* previous recursive calls */
obstack_free(&temp_strings, ob_dummy);
if (tree == NULL)
return(1);
switch (tree->type)
{
#ifndef FAST
/* Can't run these! */
case NODE_ILLEGAL:
case NODE_RULE_NODE:
case NODE_IF_BRANCHES:
case NODE_EXPRESSION_LIST:
case NODE_K_BEGIN:
case NODE_K_END:
case NODE_REDIRECT_OUTPUT:
case NODE_REDIRECT_APPEND:
case NODE_REDIRECT_PIPE:
case NODE_VAR_ARRAY:
case NODE_CONDEXP_BRANCHES:
panic("Illegal node type (%d) in interpret()", tree->type);
#endif
case NODE_RULE_LIST:
for (t = tree; t != NULL; t = t->rnode)
{
switch (setjmp(rule_tag))
{
case 0: /* normal non-jump */
if (eval_condition(t->lnode->lnode))
{
DEBUG(("Found a rule:%p", (FPTR)t->lnode->rnode));
if (t->lnode->rnode == NULL)
{
/* special case: pattern with no action is
* equivalent to an action of {print} (jfw) */
NODE printnode;
printnode.type = NODE_K_PRINT;
printnode.lnode = NULL;
printnode.rnode = NULL;
hack_print_node(&printnode);
}
else
(void) interpret(t->lnode->rnode);
}
break;
case TAG_CONTINUE: /* NEXT statement */
return(1);
case TAG_BREAK:
return(0);
}
}
break;
case NODE_STATEMENT_LIST:
/* print_a_node(tree); */
/* because BEGIN and END do not have Node_rule_list nature, yet
* can have exits and nexts, we special-case a setjmp of rule_tag
* here. (jfw) */
if (tree == begin_block || tree == end_block)
{
switch (setjmp(rule_tag))
{
case TAG_CONTINUE: /* next */
panic("unexpected next");
return(1);
case TAG_BREAK:
return(0);
}
}
for (t = tree; t != NULL; t = t->rnode)
{
DEBUG(("Statements:%p", (FPTR) t->lnode));
(void) interpret(t->lnode);
}
break;
case NODE_K_IF:
DEBUG(("IF:%p", (FPTR) tree->lnode));
if (eval_condition(tree->lnode))
{
DEBUG(("True:%p", (FPTR) tree->rnode->lnode));
(void) interpret(tree->rnode->lnode);
}
else
{
DEBUG(("False:%p", (FPTR) tree->rnode->rnode));
(void) interpret(tree->rnode->rnode);
}
break;
case NODE_K_WHILE:
PUSH_BINDING(loop_tag_stack, loop_tag);
DEBUG(("WHILE:%p", (FPTR) tree->lnode));
while (eval_condition(tree->lnode))
{
switch (setjmp(loop_tag))
{
case 0: /* normal non-jump */
DEBUG(("DO:%p", (FPTR) tree->rnode));
(void) interpret(tree->rnode);
break;
case TAG_CONTINUE: /* continue statement */
break;
case TAG_BREAK: /* break statement */
RESTORE_BINDING(loop_tag_stack, loop_tag);
return(1);
#ifndef FAST
default:
panic("Bad setjmp return (WHILE) - interpret()");
#endif
}
}
RESTORE_BINDING(loop_tag_stack, loop_tag);
break;
case NODE_K_FOR:
PUSH_BINDING(loop_tag_stack, loop_tag);
DEBUG(("FOR:%p", (FPTR) tree->forloop->init));
(void) interpret(tree->forloop->init);
DEBUG(("FOR.WHILE:%p", (FPTR) tree->forloop->cond));
while (eval_condition(tree->forloop->cond))
{
switch (setjmp(loop_tag))
{
case 0: /* normal non-jump */
DEBUG(("FOR.DO:%p", (FPTR) tree->lnode));
(void) interpret(tree->lnode);
/* fall through */
case TAG_CONTINUE: /* continue statement */
DEBUG(("FOR.INCR:%p", (FPTR)tree->forloop->incr));
(void) interpret(tree->forloop->incr);
break;
case TAG_BREAK: /* break statement */
RESTORE_BINDING(loop_tag_stack, loop_tag);
return(1);
#ifndef FAST
default:
panic("Bad setjmp return (FOR.WHILE) - interpret()");
#endif
}
}
RESTORE_BINDING(loop_tag_stack, loop_tag);
break;
case NODE_K_ARRAYFOR:
PUSH_BINDING(loop_tag_stack, loop_tag);
DEBUG(("AFOR.VAR:%p", (FPTR) tree->forloop->init));
lhs = get_lhs(tree->forloop->init);
do_deref();
for (l = assoc_scan(tree->forloop->incr); l; l = assoc_next(l))
{
*lhs = dupnode(l->retval);
DEBUG(("AFOR.NEXTIS:%p", (FPTR) *lhs));
switch (setjmp(loop_tag))
{
case 0:
DEBUG(("AFOR.DO:%p", (FPTR) tree->lnode));
(void) interpret(tree->lnode);
case TAG_CONTINUE:
break;
case TAG_BREAK:
RESTORE_BINDING(loop_tag_stack, loop_tag);
return(1);
#ifndef FAST
default:
panic("Bad setjmp return (AFOR.NEXT) - interpret()");
#endif
}
}
RESTORE_BINDING(loop_tag_stack, loop_tag);
break;
case NODE_K_BREAK:
DEBUG(("BREAK:"));
if (loop_tag_valid == 0) /* jfw */
panic("unexpected break or continue");
longjmp(loop_tag, TAG_BREAK);
break;
case NODE_K_CONTINUE:
DEBUG(("CONTINUE:"));
if (loop_tag_valid == 0) /* jfw */
panic("unexpected break or continue");
longjmp(loop_tag, TAG_CONTINUE);
break;
case NODE_K_PRINT:
DEBUG(("PRINT:%p", (FPTR) tree));
(void) hack_print_node(tree);
break;
case NODE_K_PRINTF:
DEBUG(("PRINTF:%p", (FPTR) tree));
(void) do_printf(tree);
break;
case NODE_K_NEXT:
DEBUG(("NEXT:"));
longjmp(rule_tag, TAG_CONTINUE);
break;
case NODE_K_EXIT:
/* The unix awk doc says to skip the rest of the input. Does
* that mean after performing all the rules on the current line?
* Unix awk quits immediately, so this does too. */
/* The UN*X exit can also take an optional arg return code. We
* don't */
/* Well, we parse it, but never *DO* it */
DEBUG(("EXIT:"));
longjmp(rule_tag, TAG_BREAK);
break;
case NODE_K_DELETE:
assoc_lookup(tree->lnode, tree->rnode, ASSOC_DELETE);
break;
default:
/* Appears to be an expression statement. Throw away the value. */
DEBUG(("Exp:"));
(void) tree_eval(tree);
break;
}
return(1);
}
/* evaluate a subtree, allocating strings on a temporary stack. */
/* This used to return a whole NODE, instead of a ptr to one, but that
led to lots of obnoxious copying.